package be.destin.adp;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.CharBuffer;
import java.util.Stack;

public class AdpExec {
	CharBuffer inputBuffer;
	int bufRead = 0;
	int here = 0;
	Reader inputReader;
	Writer out;
	Exception error = null;
	int bufSize;
	Stack<AdpTransformationExec> transformationExecs = new Stack<AdpTransformationExec>(); 

	AdpExec (Reader input, Writer output, int bufSize) {
		setBufSize(bufSize);
		inputBuffer = CharBuffer.allocate(this.bufSize*2);
		setReader(input);
		setWriter(output);
	}

	public boolean start(AdpTransformation transformation) {
		try {
			bufRead = getReader().read(inputBuffer);
			if (bufRead <= 0) return false;
			here = 0;
		} catch (IOException IOerror) {
			error = IOerror;
			return false;
		}
		this.call(transformation);
		return true;
	}

	public void call(AdpTransformation transformation) {
		AdpTransformationExec envTrans = new AdpTransformationExec(this,transformation);
		this.push(envTrans);
		boolean ok;
		/*TODO: complete code!!!
		do {
			if (this.checkHere()) {
				ok = true;
			} else {
				ok = this.fallback();
			}
		} while ok;
		*/
	}
	
	boolean fallback() {
		AdpTransformationExec envTrans = this.getTransformationExecs().peek();
		AdpWINdata winData = envTrans.getTransformation().getFallback();
		switch (winData.getWin()) {
		case WIN_sub: return false; // , (sub rule) : go on with a sub-transformation
		case WIN_continue:  // . Restart evaluation with first rule of the transformation
			return false;
		case WIN_break: return false; // !. Stop Input processing
		case WIN_return: /*out.write(inputBuffer.readall);*/ return false; // !! Return to the caller if any. If none, copy all input to output
		case WIN_call: break; // !transf  Call another transformation
		case WIN_switch: break; // !*transf  Switch to another transformation
		case WIN_chain: break; // ... Follow with rules of another transformation
		case WIN_copy: // Copy one character from input to output and restart evaluation with first rule of the transformation
			/*out.write(inputBuffer.charAt(inputBuffer.position()));*/
			return this.advance();
		case WIN_skip: // * Skip one character from input and restart evaluation with first rule of the transformation
				return this.advance();
		}
		return false;
	}
	
	protected boolean advance() {
		try {
			inputBuffer.position(inputBuffer.position()+1);
			return true;
		} catch (IllegalArgumentException IAE) {
			return false;
		}
	}
	/**
	 * @return the bufSize
	 */
	public int getBufSize() {
		return bufSize;
	}

	/**
	 * @param bufSize the bufSize to set
	 */
	public void setBufSize(int bufSize) {
		if (bufSize < 256) this.bufSize = 256; // a rather minimal buffer!
		else this.bufSize = bufSize;
	}

	/**
	 * @return the inputBuffer
	 */
	public CharBuffer getInputBuffer() {
		return inputBuffer;
	}
	/**
	 * @param inputBuffer the inputBuffer to set
	 */
	public void setInputBuffer(CharBuffer inputBuffer) {
		this.inputBuffer = inputBuffer;
	}
	/**
	 * @return the inputReader
	 */
	public Reader getReader() {
		return inputReader;
	}
	/**
	 * @param inputReader the inputReader to set
	 */
	public void setReader(Reader inputReader) {
		this.inputReader = inputReader;
	}
	/**
	 * @return the out
	 */
	public Writer getWriter() {
		return out;
	}
	/**
	 * @param out the out to set
	 */
	public void setWriter(Writer out) {
		this.out = out;
	}
	/**
	 * @return the transformationExecs
	 */
	public Stack<AdpTransformationExec> getTransformationExecs() {
		return transformationExecs;
	}
	/**
	 * @param transformationExecs the transformationExecs to set
	 */
	public void setTransformationExecs(
			Stack<AdpTransformationExec> transformationExecs) {
		this.transformationExecs = transformationExecs;
	}

	public void push(AdpTransformationExec newTransformation) {
		this.transformationExecs.push(newTransformation);
	}
	public AdpTransformationExec pop() {
		return this.transformationExecs.pop();
	}
	public AdpTransformationExec peek() {
		return this.transformationExecs.peek();
	}
}
